package com.evaluation.kexingcp.psycho.evaluation.sys.sms

import arrow.core.Either
import arrow.core.raise.Raise
import arrow.core.raise.catch
import arrow.core.raise.either
import arrow.core.raise.ensure
import cn.hutool.core.util.RandomUtil
import com.alibaba.fastjson.JSON
import com.baomidou.mybatisplus.core.toolkit.StringPool
import com.evaluation.kexingcp.common.core.constant.CacheConstants
import com.evaluation.kexingcp.common.core.constant.SecurityConstants
import com.evaluation.kexingcp.common.core.constant.enums.LoginTypeEnum
import com.evaluation.kexingcp.common.core.util.SpringContextHolder
import com.evaluation.kexingcp.common.websocket.distribute.MessageDO
import com.evaluation.kexingcp.common.websocket.distribute.RedisMessageDistributor
import com.tencentcloudapi.ses.v20201002.SesClient
import com.tencentcloudapi.ses.v20201002.models.SendEmailRequest
import com.tencentcloudapi.ses.v20201002.models.Template
import com.tencentcloudapi.sms.v20210111.SmsClient
import org.apache.commons.lang3.RegExUtils
import org.slf4j.LoggerFactory
import org.springframework.data.redis.core.StringRedisTemplate
import org.springframework.stereotype.Service
import java.util.concurrent.TimeUnit

sealed interface SmsError {
    val message: String
}

class SmsValidationError(override val message: String) : SmsError
class SmsSendError(override val message: String) : SmsError

data class WebSocketMsg(val type: String, val msg: String)

interface SmsService {
    fun sendRegisterSms(mobile: String): Either<SmsError, Unit>
    fun sendRegisterSuccessSms(mobile: String, name:String): Either<SmsError, Unit>
    fun sendRegisterEmail(email: String, orgName:String, account:String, url: String, password: String?): Either<SmsError, Unit>
    fun sendNoticeEvaluationSms(mobile: String) : Either<SmsError, Unit>
    fun sendWebSocketMsg(userIds: List<Long>, msg: WebSocketMsg): Either<SmsError, Unit>
}


@Service
class SmsServiceImpl(
    private val smsClient: SmsClient,
    private val sesClient: SesClient,
    private val redisTemplate: StringRedisTemplate
) : SmsService {

    private val log = LoggerFactory.getLogger(SmsServiceImpl::class.java)

    context(Raise<SmsError>)
    private fun mobileValidation(mobile: String) {
        ensure(mobile.isNotBlank()) { SmsValidationError("手机号不能为空") }
        ensure(RegExUtils.dotAllMatcher("^[1][0-9][0-9]{9}$",mobile).find()) { SmsValidationError("手机号格式错误") }
    }

    context(Raise<SmsError>)
    private fun validation(mobile: String, cacheKey: String) {
        mobileValidation(mobile)

        val codeObj = redisTemplate.opsForValue().get(cacheKey);
        ensure(codeObj == null) {
            log.info("手机号验证码未过期:{}，{}", mobile, codeObj)
            SmsValidationError("手机号验证码未过期: $mobile")
        }
    }

    override fun sendRegisterSms(mobile: String): Either<SmsError, Unit> = either {
        val cacheKey = CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.REG.type + StringPool.AT + mobile
        validation(mobile, cacheKey)

        val code = RandomUtil.randomNumbers(4)

        sendSms(mobile, templateId = "2136570", params = arrayOf(code))
        log.info("手机号生成验证码成功:{},{}", mobile, code);
        redisTemplate.opsForValue().set(cacheKey, code, SecurityConstants.CODE_TIME.toLong(), TimeUnit.SECONDS);
    }

    override fun sendRegisterSuccessSms(mobile: String, name:String): Either<SmsError, Unit> = either {
        sendSms(mobile, templateId = "2196779", params = arrayOf(name))
    }

    override fun sendRegisterEmail(email: String, orgName:String, account:String, url: String, password: String?): Either<SmsError, Unit>
    = either {
        val request = SendEmailRequest()
        request.subject = "注册成功通知邮件"
        request.destination = arrayOf(email)
        request.fromEmailAddress = "kxypt@mail.sxkx.cn"
        request.template = Template().also {
            it.templateData = """
                {
                    "corporatename":"$orgName",
                    "username":"$orgName",
                    "account":"$account",
                    "password": "${password?:"123456"}",
                    "name":"$url"
                }
            """.trimIndent()
            it.templateID = 28814
        }

        catch({ sesClient.SendEmail(request) }) { e ->
            log.error("邮件发送失败: {}", e.message, e)
            raise(SmsSendError(e.message ?: "邮件发送失败"))
        }
    }

    override fun sendNoticeEvaluationSms(mobile: String): Either<SmsError, Unit> = either {
        mobileValidation(mobile)
        sendSms(mobile, templateId = "2136681", params = arrayOf())
    }

    override fun sendWebSocketMsg(userIds: List<Long>, msg: WebSocketMsg): Either<SmsError, Unit> = either {
        val messageDistributor: RedisMessageDistributor =
            SpringContextHolder.getBean(RedisMessageDistributor::class.java)
        // websocket 发送消息
        val messageDO = MessageDO()
        messageDO.setNeedBroadcast(java.lang.Boolean.FALSE)
        // 给目标用户ID
        messageDO.setSessionKeys(userIds)
        messageDO.setMessageText(JSON.toJSONString(msg))
        messageDistributor.distribute(messageDO)
    }

    context(Raise<SmsError>)
    private fun sendSms(mobile: String, templateId: String, params: Array<String>) {
        val request = com.tencentcloudapi.sms.v20210111.models.SendSmsRequest()
        request.phoneNumberSet = arrayOf(mobile)
        request.templateId = templateId
        request.templateParamSet = params
        request.signName = "科星心理测评云平台网"
        request.smsSdkAppId = "1400893266"

        val response = catch({ smsClient.SendSms(request) }) { e ->
            log.error("短信发送失败: {}", e.message, e)
            raise(SmsSendError(e.message ?: "短信发送失败"))
        }

        if (response.sendStatusSet.isEmpty()) {
            log.error("短信发送失败 :{}", response.toString())
            raise(SmsSendError("短信发送失败"))
        }

        response.sendStatusSet.forEach { status ->
            if (status.code != "Ok") {
                log.error("短信发送失败 :{} {}", status.code, status.message)
                raise(SmsSendError("短信发送失败"))
            }
        }
    }
}